#include <chrono> 
#include "Log/SystemLog.h"
#include "ModuleLoader/ModuleConfig.h"
#include "Model/Video/VideoSoftDecoder/VideoSoftDecoder.h"
#include "Model/Video/VideoHardDecoder/VideoHardDecoder.h"
#include "VideoDecoder.h"

using namespace std::chrono;

REGISTER_MODULE(IVideoDecoder, VideoDecoder)

// Construct the VideoDecoder
VideoDecoder::VideoDecoder() :
	m_pSoftDecoder(NULL),
	m_pHardDecoder(NULL),
	m_bIsUseHardDecoder(false),
	m_bDisposed(false)
{
	Initialize();
}

// Detructe the VideoDecoder
VideoDecoder::~VideoDecoder()
{
	Destory();
}

// Initialize the decoder
void VideoDecoder::Initialize()
{
	// Create soft decoder
	CreateSoftDecoder();

	// Create hard decoder
	CreateHardDecoder();
}

// Destory the decoder
void VideoDecoder::Destory()
{
	if (!GetDisposed())
	{
		SetDisposed(true);

		// Destory hard decoder
		DestoryHardDecoder();

		// Destory soft decoder
		DestorySoftDecoder();
	}
}

// Create soft decoder
void VideoDecoder::CreateSoftDecoder()
{
	SetSoftDecoder(new VideoSoftDecoder());
}

// Destory soft decoder
void VideoDecoder::DestorySoftDecoder()
{
	if (GetSoftDecoder())
	{
		delete GetSoftDecoder();

		SetSoftDecoder(NULL);
	}
}

// Create hard decoder
void VideoDecoder::CreateHardDecoder()
{
	SetHardDecoder(new VideoHardDecoder());
}

// Destory hard decoder
void VideoDecoder::DestoryHardDecoder()
{
	if (GetHardDecoder())
	{
		delete GetHardDecoder();

		SetHardDecoder(NULL);
	}
}

// Is current system support hard decoder
bool VideoDecoder::IsSupportHardDecode(VideoTypeEnum eVideoType)
{
	if (GetHardDecoder() == NULL)
	{
		return false;
	}

	return GetHardDecoder()->IsSupportHardDecode(eVideoType);
}

// Init soft decoder
bool VideoDecoder::InitSoftDecoder(VideoTypeEnum eVideoType)
{
	if (GetSoftDecoder() == NULL)
	{
		LOG_ERROR_EX("Please create soft decoder at first!");

		return false;
	}

	return GetSoftDecoder()->Init(eVideoType);
}

// Init hard decoder
bool VideoDecoder::InitHardDecoder(VideoTypeEnum eVideoType, void* pWindowHandle)
{
	if (GetHardDecoder() == NULL)
	{
		LOG_ERROR_EX("Please create hard decoder at first!");

		return false;
	}

	return GetHardDecoder()->Init(eVideoType, pWindowHandle);
}

// Init the decoder
bool VideoDecoder::Init(VideoTypeEnum eVideoType, void* pWindowHandle)
{
	if (!IsSupportHardDecode(eVideoType))
	{
		// Init soft decoder
		if (!InitSoftDecoder(eVideoType))
		{
			return false;
		}

		SetIsUseHardDecoder(false);

		return true;
	}
	
	// Init hard decoder
	if (!InitHardDecoder(eVideoType, pWindowHandle))
	{
		return false;
	}

	SetIsUseHardDecoder(true);

	return true;
}

// Flush the decode at the last frame decoding
void VideoDecoder::Flush()
{
	// Hard decode
	if (GetIsUseHardDecoder())
	{
		if (GetHardDecoder() == NULL)
		{
			LOG_ERROR_EX("Create hard decoder at first !");

			return;
		}

		GetHardDecoder()->Flush();
	}

	// Soft decode
	if (GetSoftDecoder() == NULL)
	{
		LOG_ERROR_EX("Create soft decoder at first !");

		return;
	}

	GetSoftDecoder()->Flush();
}

// Excute the decoding
bool VideoDecoder::Excute(uint8_t* pSrcData,
	int iSrcSize,
	uint8_t* pDst,
	int& iDstSize,
	int& iFrameWidth,
	int& iFrameHeight)
{
	if (pSrcData == NULL)
	{
		LOG_ERROR_EX("Source data is null !");

		return false;
	}

	if (iSrcSize <= 0)
	{
		LOG_ERROR_EX("Source data size <= 0 !");

		return false;
	}

	if (pDst == NULL)
	{
		LOG_ERROR_EX("Dest buffer is null,you must alloc dest buffer at first !");

		return false;
	}

	// Hard decode
	if (GetIsUseHardDecoder())
	{
		if (GetHardDecoder() == NULL)
		{
			LOG_ERROR_EX("Create hard decoder at first !");

			return false;
		}

		auto startTime = system_clock::now();

		if (!GetHardDecoder()->Excute(pSrcData,
			iSrcSize,
			pDst,
			iDstSize,
			iFrameWidth,
			iFrameHeight))
		{
			return false;
		}

		auto endTime = system_clock::now();

		auto duration = std::chrono::duration_cast<std::chrono::microseconds>(endTime - startTime);

		std::cout << "Total hard decode time:"
			<< duration.count()
			<< "us"
			<< std::endl;

		return true;
	}

	// Soft decode
	if (GetSoftDecoder() == NULL)
	{
		LOG_ERROR_EX("Create soft decoder at first !");

		return false;
	}

	auto startTime = system_clock::now();

	if (!GetSoftDecoder()->Excute(pSrcData,
		iSrcSize,
		pDst,
		iDstSize,
		iFrameWidth,
		iFrameHeight))
	{
		return false;
	}

	auto endTime = system_clock::now();

	auto duration = std::chrono::duration_cast<std::chrono::microseconds>(endTime - startTime);

	std::cout << "Total soft decode time:"
		<< duration.count()
		<< "us"
		<< std::endl;

	return true;
}
